This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

数值化

ds2训练分类器

ds2 -> ds1

predict_prop_ds1 <- matrix(data=predict_ds1_test, nrow = length(levels(Idents(ds2))), 
                           ncol = ncol(ds1), byrow = FALSE, 
                           dimnames = list(levels(Idents(ds2)),colnames(ds1)))

## 得到分群结果
ds1_res <- apply(predict_prop_ds1,2,func,rownames(predict_prop_ds1))
Idents(ds1) <- factor(ds1_res,levels = c(0:4))
umapplot(ds1)

数值化地投射回umap

embedding <- FetchData(object = ds1, vars = c("UMAP_1", "UMAP_2"))
embedding <- cbind(embedding, t(predict_prop_ds1))

ggobj <- ggplot() +
  geom_point(data = embedding[embedding$`0`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `0`), shape=16, size = 3, alpha=0.5) + 
  scale_color_gradient('0', low = "#FFFFFF00", high = "#6dc0a6") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`1`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `1`), size = 3, alpha=0.5) + 
  scale_color_gradient('1', low = "#FFFFFF00", high = "#e2b398") +
   new_scale("color") +
    geom_point(data = embedding[embedding$`2`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `2`), size = 2, alpha=0.5) + 
  scale_color_gradient('2', low = "#FFFFFF00", high = "#e2a2ca") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`3`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `3`), size = 3, alpha=0.5) + 
  scale_color_gradient('3', low = "#FFFFFF00", high = "#d1eba8") +
   new_scale("color") +
      geom_point(data = embedding[embedding$`4`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `4`), size = 3, alpha=0.5) + 
  scale_color_gradient('4', low = "#FFFFFF00", high = "#b1d6fb") +
    new_scale("color") +
        xlab("UMAP 1") + ylab("UMAP 2")  +
        theme(axis.line = element_line(arrow = arrow(length = unit(0.2, "cm")))) +
        scale_y_continuous(breaks = NULL) +
        scale_x_continuous(breaks = NULL) + 
  theme(panel.background = element_blank(), panel.grid = element_blank(), legend.position = "bottom")
ggsave("pre_ds1_umap.svg",device = svg,plot = ggobj,height = 10,width = 10)

#ds2 -> ds0

Idents(ds0) <- ds0$seurat_clusters
temp <- get_data_table(ds0, highvar = F, type = "data")
ds0_data <- matrix(data=0, nrow = length(rownames(ds2_data)), ncol = length(colnames(temp)), 
                   byrow = FALSE, dimnames = list(rownames(ds2_data),colnames(temp)))
for(i in intersect(rownames(ds2_data), rownames(temp))){
  ds0_data[i,] <- temp[i,]
}
rm(temp)
ds0_label <- as.numeric(as.character(Idents(ds0)))
colnames(ds0_data) <- NULL
ds0_test_data <- list(data = t(as(ds0_data,"dgCMatrix")), label = ds0_label)
ds0_test <- xgb.DMatrix(data = ds0_test_data$data,label = ds0_test_data$label)

#预测结果

predict_ds0_test <- predict(bst_model, newdata = ds0_test)

predict_prop_ds0 <- matrix(data=predict_ds0_test, nrow = length(levels(Idents(ds2))), 
                           ncol = ncol(ds0), byrow = FALSE, 
                           dimnames = list(levels(Idents(ds2)),colnames(ds0)))

## 得到分群结果
ds0_res <- apply(predict_prop_ds0,2,func,rownames(predict_prop_ds0))
Idents(ds0) <- factor(ds0_res,levels = c(0:4))
umapplot(ds0)

embedding <- FetchData(object = ds0, vars = c("UMAP_1", "UMAP_2"))
embedding <- cbind(embedding, t(predict_prop_ds0))

ggobj <- ggplot() +
  geom_point(data = embedding[embedding$`0`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `0`), shape=16, size = 3, alpha=0.5) + 
  scale_color_gradient('0', low = "#FFFFFF00", high = "#6dc0a6") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`1`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `1`),shape=16, size = 3, alpha=0.5) + 
  scale_color_gradient('1', low = "#FFFFFF00", high = "#e2b398") +
   new_scale("color") +
    geom_point(data = embedding[embedding$`2`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `2`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('2', low = "#FFFFFF00", high = "#e2a2ca") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`3`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `3`),shape=16, size = 3, alpha=0.5) + 
  scale_color_gradient('3', low = "#FFFFFF00", high = "#d1eba8") +
   new_scale("color") +
      geom_point(data = embedding[embedding$`4`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `4`),shape=16, size = 3, alpha=0.5) + 
  scale_color_gradient('4', low = "#FFFFFF00", high = "#b1d6fb") +
    new_scale("color") +
        xlab("UMAP 1") + ylab("UMAP 2")  +
        theme(axis.line = element_line(arrow = arrow(length = unit(0.2, "cm")))) +
        scale_y_continuous(breaks = NULL) +
        scale_x_continuous(breaks = NULL) + 
  theme(panel.background = element_blank(), panel.grid = element_blank(), legend.position = "bottom")
ggsave("pre_ds1_umap.svg",device = svg,plot = ggobj,height = 10,width = 10)

PA -> AC

selected_features <- read.csv("./datatable/selected_features.csv", stringsAsFactors = F)
selected_features <- selected_features$x
PA_data <- get_data_table(ds2_PA, highvar = F, type = "data")
PA_data <- PA_data[selected_features,]
PA_label <- as.numeric(as.character(Idents(ds2_PA)))
colnames(PA_data) <- NULL

PA_train_data <- list(data = t(as(PA_data,"dgCMatrix")), label = PA_label)
PA_train <- xgb.DMatrix(data = PA_train_data$data,label = PA_train_data$label)
xgb_param <- list(eta = 0.2, max_depth = 6, 
                  subsample = 0.6,  num_class = length(table(Idents(ds2_PA))),
                  objective = "multi:softprob", eval_metric = 'mlogloss')

bst_model <- xgb.train(xgb_param, PA_train, nrounds = 100, verbose = 0)
Idents(ds2_AC) <- ds2_AC$seurat_clusters
AC_data <- get_data_table(ds2_AC, highvar = F, type = "data")
AC_data <- AC_data[selected_features,]
AC_label <- as.numeric(as.character(Idents(ds2_AC)))
colnames(AC_data) <- NULL
AC_test_data <- list(data = t(as(AC_data,"dgCMatrix")), label = AC_label)
AC_test <- xgb.DMatrix(data = AC_test_data$data,label = AC_test_data$label)

#预测结果
predict_prop_AC <-predict(bst_model, newdata = AC_test) %>%
 matrix(nrow = length(levels(Idents(ds2_PA))), 
                           ncol = ncol(ds2_AC), byrow = FALSE, 
                           dimnames = list(levels(Idents(ds2_PA)),colnames(ds2_AC)))

## 得到分群结果
AC_res <- apply(predict_prop_AC,2,func,rownames(predict_prop_AC))
Idents(ds2_AC) <- AC_res
umapplot(ds2_AC)

embedding <- FetchData(object = ds2_AC, vars = c("UMAP_1", "UMAP_2"))
embedding <- cbind(embedding, t(predict_prop_AC))

ggobj <- ggplot() +
  geom_point(data = embedding[embedding$`0`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `0`), shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('0', low = "#FFFFFF00", high = "#6dc0a6") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`1`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `1`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('1', low = "#FFFFFF00", high = "#e2b398") +
   new_scale("color") +
    geom_point(data = embedding[embedding$`2`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `2`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('2', low = "#FFFFFF00", high = "#e2a2ca") +
        xlab("UMAP 1") + ylab("UMAP 2")  +
        theme(axis.line = element_line(arrow = arrow(length = unit(0.2, "cm")))) +
        scale_y_continuous(breaks = NULL) +
        scale_x_continuous(breaks = NULL) + 
  theme(panel.background = element_blank(), panel.grid = element_blank(), legend.position = "bottom")
ggsave("ds2_PAtoAC_umap.svg",device = svg,plot = ggobj,height = 8,width = 8)

在ds0上训练

ds0_data <- get_data_table(ds0, highvar = F, type = "data")
ds0_label <- as.numeric(as.character(Idents(ds0)))

index <- c(1:dim(ds0_data)[2]) %>% sample(ceiling(0.3*dim(ds0_data)[2]), replace = F, prob = NULL)
colnames(ds0_data) <- NULL

ds0_train_data <- list(data = t(as(ds0_data[,-index],"dgCMatrix")), label = ds0_label[-index])
ds0_test_data <- list(data = t(as(ds0_data[,index],"dgCMatrix")), label = ds0_label[index])

ds0_train <- xgb.DMatrix(data = ds0_train_data$data,label = ds0_train_data$label)
ds0_test <- xgb.DMatrix(data = ds0_test_data$data,label = ds0_test_data$label)

watchlist <- list(train = ds0_train, eval = ds0_test)
xgb_param <- list(eta = 0.2, max_depth = 6, 
                  subsample = 0.6,  num_class = length(table(Idents(ds0))),
                  objective = "multi:softprob", eval_metric = 'mlogloss')

bst_model <- xgb.train(xgb_param, ds0_train, nrounds = 100, watchlist, verbose = 0)

eval_loss <- bst_model[["evaluation_log"]][["eval_mlogloss"]]
plot_ly(data.frame(eval_loss), x = c(1:100), y = eval_loss) %>% 
  add_trace(type = "scatter", mode = "markers+lines", 
            marker = list(color = "black", line = list(color = "#1E90FFC7", width = 1)),
            line = list(color = "#1E90FF80", width = 2)) %>% 
  layout(xaxis = list(title = "epoch"),yaxis = list(title = "eval_mlogloss"))
importance <- xgb.importance(colnames(ds0_train), model = bst_model)
head(importance)
xgb.ggplot.importance(head(importance,20), n_clusters = 1) + theme_minimal()


multi_featureplot(head(importance,9)$Feature, ds0) 

embedding <- cbind(embedding, t(predict_prop_ds2))
Error in data.frame(..., check.names = FALSE) : 
  arguments imply differing number of rows: 3059, 9537
embedding <- FetchData(object = ds1, vars = c("UMAP_1", "UMAP_2"))
embedding <- cbind(embedding, t(predict_prop_ds1))

ggobj <- ggplot() +
  geom_point(data = embedding[embedding$`0`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `0`), shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('0', low = "#FFFFFF00", high = "#6dc0a6") +
  new_scale("color") +
    geom_point(data = embedding[embedding$`1`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `1`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('1', low = "#FFFFFF00", high = "#e2b398") +
   new_scale("color") +
    geom_point(data = embedding[embedding$`2`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `2`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('2', low = "#FFFFFF00", high = "#e2a2ca") +
     new_scale("color") +
    geom_point(data = embedding[embedding$`3`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `3`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('3', low = "#FFFFFF00", high = "#d1eba8") +
     new_scale("color") +
    geom_point(data = embedding[embedding$`4`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `4`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('4', low = "#FFFFFF00", high = "#b1d6fb") +
     new_scale("color") +
    geom_point(data = embedding[embedding$`5`>0.1,], 
             aes(x = UMAP_1, y = UMAP_2, color = `5`),shape=16, size = 2, alpha=0.5) + 
  scale_color_gradient('5', low = "#FFFFFF00", high = "#fd9999") +
        xlab("UMAP 1") + ylab("UMAP 2")  +
        theme(axis.line = element_line(arrow = arrow(length = unit(0.2, "cm")))) +
        scale_y_continuous(breaks = NULL) +
        scale_x_continuous(breaks = NULL) + 
  theme(panel.background = element_blank(), panel.grid = element_blank(), legend.position = "bottom")
ggsave("ds0tods1umap.svg",device = svg,plot = ggobj,height = 8,width = 8)

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIAoKIyMg5pWw5YC85YyWCiMjIyBkczLorq3nu4PliIbnsbvlmagKYGBge3J9CmRzMl9kYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMiwgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpCmRzMl9sYWJlbCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihJZGVudHMoZHMyKSkpCgppbmRleCA8LSBjKDE6ZGltKGRzMl9kYXRhKVsyXSkgJT4lIHNhbXBsZShjZWlsaW5nKDAuMypkaW0oZHMyX2RhdGEpWzJdKSwgcmVwbGFjZSA9IEYsIHByb2IgPSBOVUxMKQpjb2xuYW1lcyhkczJfZGF0YSkgPC0gTlVMTAoKZHMyX3RyYWluX2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhkczJfZGF0YVssLWluZGV4XSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IGRzMl9sYWJlbFstaW5kZXhdKQpkczJfdGVzdF9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoZHMyX2RhdGFbLGluZGV4XSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IGRzMl9sYWJlbFtpbmRleF0pCgpkczJfdHJhaW4gPC0geGdiLkRNYXRyaXgoZGF0YSA9IGRzMl90cmFpbl9kYXRhJGRhdGEsbGFiZWwgPSBkczJfdHJhaW5fZGF0YSRsYWJlbCkKZHMyX3Rlc3QgPC0geGdiLkRNYXRyaXgoZGF0YSA9IGRzMl90ZXN0X2RhdGEkZGF0YSxsYWJlbCA9IGRzMl90ZXN0X2RhdGEkbGFiZWwpCgp3YXRjaGxpc3QgPC0gbGlzdCh0cmFpbiA9IGRzMl90cmFpbiwgZXZhbCA9IGRzMl90ZXN0KQp4Z2JfcGFyYW0gPC0gbGlzdChldGEgPSAwLjIsIG1heF9kZXB0aCA9IDYsIAogICAgICAgICAgICAgICAgICBzdWJzYW1wbGUgPSAwLjYsICBudW1fY2xhc3MgPSBsZW5ndGgodGFibGUoSWRlbnRzKGRzMikpKSwKICAgICAgICAgICAgICAgICAgb2JqZWN0aXZlID0gIm11bHRpOnNvZnRwcm9iIiwgZXZhbF9tZXRyaWMgPSAnbWxvZ2xvc3MnKQoKYnN0X21vZGVsIDwtIHhnYi50cmFpbih4Z2JfcGFyYW0sIGRzMl90cmFpbiwgbnJvdW5kcyA9IDEwMCwgd2F0Y2hsaXN0LCB2ZXJib3NlID0gMCkKCmV2YWxfbG9zcyA8LSBic3RfbW9kZWxbWyJldmFsdWF0aW9uX2xvZyJdXVtbImV2YWxfbWxvZ2xvc3MiXV0KcGxvdF9seShkYXRhLmZyYW1lKGV2YWxfbG9zcyksIHggPSBjKDE6MTAwKSwgeSA9IGV2YWxfbG9zcykgJT4lIAogIGFkZF90cmFjZSh0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMrbGluZXMiLCAKICAgICAgICAgICAgbWFya2VyID0gbGlzdChjb2xvciA9ICJibGFjayIsIGxpbmUgPSBsaXN0KGNvbG9yID0gIiMxRTkwRkZDNyIsIHdpZHRoID0gMSkpLAogICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICIjMUU5MEZGODAiLCB3aWR0aCA9IDIpKSAlPiUgCiAgbGF5b3V0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJlcG9jaCIpLHlheGlzID0gbGlzdCh0aXRsZSA9ICJldmFsX21sb2dsb3NzIikpCmBgYAojIyBkczIgLT4gZHMxCmBgYHtyfQp0ZW1wIDwtIGdldF9kYXRhX3RhYmxlKGRzMSwgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpCmRzMV9kYXRhIDwtIG1hdHJpeChkYXRhPTAsIG5yb3cgPSBsZW5ndGgocm93bmFtZXMoZHMyX2RhdGEpKSwgbmNvbCA9IGxlbmd0aChjb2xuYW1lcyh0ZW1wKSksIAogICAgICAgICAgICAgICAgICAgYnlyb3cgPSBGQUxTRSwgZGltbmFtZXMgPSBsaXN0KHJvd25hbWVzKGRzMl9kYXRhKSxjb2xuYW1lcyh0ZW1wKSkpCmZvcihpIGluIGludGVyc2VjdChyb3duYW1lcyhkczJfZGF0YSksIHJvd25hbWVzKHRlbXApKSl7CiAgZHMxX2RhdGFbaSxdIDwtIHRlbXBbaSxdCn0Kcm0odGVtcCkKZHMxX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczEpKSkKY29sbmFtZXMoZHMxX2RhdGEpIDwtIE5VTEwKZHMxX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGRzMV9kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gZHMxX2xhYmVsKQpkczFfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gZHMxX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gZHMxX3Rlc3RfZGF0YSRsYWJlbCkKCiPpooTmtYvnu5PmnpwKCnByZWRpY3RfZHMxX3Rlc3QgPC0gcHJlZGljdChic3RfbW9kZWwsIG5ld2RhdGEgPSBkczFfdGVzdCkKCnByZWRpY3RfcHJvcF9kczEgPC0gbWF0cml4KGRhdGE9cHJlZGljdF9kczFfdGVzdCwgbnJvdyA9IGxlbmd0aChsZXZlbHMoSWRlbnRzKGRzMikpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSBuY29sKGRzMSksIGJ5cm93ID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QobGV2ZWxzKElkZW50cyhkczIpKSxjb2xuYW1lcyhkczEpKSkKCiMjIOW+l+WIsOWIhue+pOe7k+aenApkczFfcmVzIDwtIGFwcGx5KHByZWRpY3RfcHJvcF9kczEsMixmdW5jLHJvd25hbWVzKHByZWRpY3RfcHJvcF9kczEpKQpJZGVudHMoZHMxKSA8LSBmYWN0b3IoZHMxX3JlcyxsZXZlbHMgPSBjKDA6NCkpCnVtYXBwbG90KGRzMSkKYGBgCgojIyDmlbDlgLzljJblnLDmipXlsITlm551bWFwCmBgYHtyfQplbWJlZGRpbmcgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IGRzMSwgdmFycyA9IGMoIlVNQVBfMSIsICJVTUFQXzIiKSkKZW1iZWRkaW5nIDwtIGNiaW5kKGVtYmVkZGluZywgdChwcmVkaWN0X3Byb3BfZHMxKSkKCmdnb2JqIDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAwYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgMGApLCBzaGFwZT0xNiwgc2l6ZSA9IDMsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMCcsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiM2ZGMwYTYiKSArCiAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDFgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAxYCksc2hhcGU9MTYsIHNpemUgPSAzLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzEnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZTJiMzk4IikgKwogICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgMmA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDJgKSxzaGFwZT0xNiwgc2l6ZSA9IDMsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMicsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNlMmEyY2EiKSArCiAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDNgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAzYCksc2hhcGU9MTYsIHNpemUgPSAzLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzMnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZDFlYmE4IikgKwogICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGA0YD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgNGApLHNoYXBlPTE2LCBzaXplID0gMywgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCc0JywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2IxZDZmYiIpICsKICAgIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICAgICAgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSAgKwogICAgICAgIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKSkpKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKyAKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKZ2dzYXZlKCJwcmVfZHMxX3VtYXAuc3ZnIixkZXZpY2UgPSBzdmcscGxvdCA9IGdnb2JqLGhlaWdodCA9IDEwLHdpZHRoID0gMTApCmBgYAoKI2RzMiAtPiBkczAKYGBge3J9CklkZW50cyhkczApIDwtIGRzMCRzZXVyYXRfY2x1c3RlcnMKdGVtcCA8LSBnZXRfZGF0YV90YWJsZShkczAsIGhpZ2h2YXIgPSBGLCB0eXBlID0gImRhdGEiKQpkczBfZGF0YSA8LSBtYXRyaXgoZGF0YT0wLCBucm93ID0gbGVuZ3RoKHJvd25hbWVzKGRzMl9kYXRhKSksIG5jb2wgPSBsZW5ndGgoY29sbmFtZXModGVtcCkpLCAKICAgICAgICAgICAgICAgICAgIGJ5cm93ID0gRkFMU0UsIGRpbW5hbWVzID0gbGlzdChyb3duYW1lcyhkczJfZGF0YSksY29sbmFtZXModGVtcCkpKQpmb3IoaSBpbiBpbnRlcnNlY3Qocm93bmFtZXMoZHMyX2RhdGEpLCByb3duYW1lcyh0ZW1wKSkpewogIGRzMF9kYXRhW2ksXSA8LSB0ZW1wW2ksXQp9CnJtKHRlbXApCmRzMF9sYWJlbCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihJZGVudHMoZHMwKSkpCmNvbG5hbWVzKGRzMF9kYXRhKSA8LSBOVUxMCmRzMF90ZXN0X2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhkczBfZGF0YSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IGRzMF9sYWJlbCkKZHMwX3Rlc3QgPC0geGdiLkRNYXRyaXgoZGF0YSA9IGRzMF90ZXN0X2RhdGEkZGF0YSxsYWJlbCA9IGRzMF90ZXN0X2RhdGEkbGFiZWwpCgoj6aKE5rWL57uT5p6cCgpwcmVkaWN0X2RzMF90ZXN0IDwtIHByZWRpY3QoYnN0X21vZGVsLCBuZXdkYXRhID0gZHMwX3Rlc3QpCgpwcmVkaWN0X3Byb3BfZHMwIDwtIG1hdHJpeChkYXRhPXByZWRpY3RfZHMwX3Rlc3QsIG5yb3cgPSBsZW5ndGgobGV2ZWxzKElkZW50cyhkczIpKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gbmNvbChkczApLCBieXJvdyA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KGxldmVscyhJZGVudHMoZHMyKSksY29sbmFtZXMoZHMwKSkpCgojIyDlvpfliLDliIbnvqTnu5PmnpwKZHMwX3JlcyA8LSBhcHBseShwcmVkaWN0X3Byb3BfZHMwLDIsZnVuYyxyb3duYW1lcyhwcmVkaWN0X3Byb3BfZHMwKSkKSWRlbnRzKGRzMCkgPC0gZmFjdG9yKGRzMF9yZXMsbGV2ZWxzID0gYygwOjQpKQp1bWFwcGxvdChkczApCmBgYApgYGB7cn0KZW1iZWRkaW5nIDwtIEZldGNoRGF0YShvYmplY3QgPSBkczAsIHZhcnMgPSBjKCJVTUFQXzEiLCAiVU1BUF8yIikpCmVtYmVkZGluZyA8LSBjYmluZChlbWJlZGRpbmcsIHQocHJlZGljdF9wcm9wX2RzMCkpCgpnZ29iaiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgMGA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDBgKSwgc2hhcGU9MTYsIHNpemUgPSAzLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzAnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjNmRjMGE2IikgKwogIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAxYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgMWApLHNoYXBlPTE2LCBzaXplID0gMywgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCcxJywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2UyYjM5OCIpICsKICAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDJgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAyYCksc2hhcGU9MTYsIHNpemUgPSAzLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzInLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZTJhMmNhIikgKwogIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAzYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgM2ApLHNoYXBlPTE2LCBzaXplID0gMywgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCczJywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2QxZWJhOCIpICsKICAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgNGA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDRgKSxzaGFwZT0xNiwgc2l6ZSA9IDMsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnNCcsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNiMWQ2ZmIiKSArCiAgICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgICAgIHhsYWIoIlVNQVAgMSIpICsgeWxhYigiVU1BUCAyIikgICsKICAgICAgICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImNtIikpKSkgKwogICAgICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBOVUxMKSArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsgCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmdnc2F2ZSgicHJlX2RzMF91bWFwLnN2ZyIsZGV2aWNlID0gc3ZnLHBsb3QgPSBnZ29iaixoZWlnaHQgPSAxMCx3aWR0aCA9IDEwKQpgYGAKCgojIFBBIC0+IEFDCmBgYHtyfQpzZWxlY3RlZF9mZWF0dXJlcyA8LSByZWFkLmNzdigiLi9kYXRhdGFibGUvc2VsZWN0ZWRfZmVhdHVyZXMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnNlbGVjdGVkX2ZlYXR1cmVzIDwtIHNlbGVjdGVkX2ZlYXR1cmVzJHgKUEFfZGF0YSA8LSBnZXRfZGF0YV90YWJsZShkczJfUEEsIGhpZ2h2YXIgPSBGLCB0eXBlID0gImRhdGEiKQpQQV9kYXRhIDwtIFBBX2RhdGFbc2VsZWN0ZWRfZmVhdHVyZXMsXQpQQV9sYWJlbCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihJZGVudHMoZHMyX1BBKSkpCmNvbG5hbWVzKFBBX2RhdGEpIDwtIE5VTEwKClBBX3RyYWluX2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhQQV9kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gUEFfbGFiZWwpClBBX3RyYWluIDwtIHhnYi5ETWF0cml4KGRhdGEgPSBQQV90cmFpbl9kYXRhJGRhdGEsbGFiZWwgPSBQQV90cmFpbl9kYXRhJGxhYmVsKQp4Z2JfcGFyYW0gPC0gbGlzdChldGEgPSAwLjIsIG1heF9kZXB0aCA9IDYsIAogICAgICAgICAgICAgICAgICBzdWJzYW1wbGUgPSAwLjYsICBudW1fY2xhc3MgPSBsZW5ndGgodGFibGUoSWRlbnRzKGRzMl9QQSkpKSwKICAgICAgICAgICAgICAgICAgb2JqZWN0aXZlID0gIm11bHRpOnNvZnRwcm9iIiwgZXZhbF9tZXRyaWMgPSAnbWxvZ2xvc3MnKQoKYnN0X21vZGVsIDwtIHhnYi50cmFpbih4Z2JfcGFyYW0sIFBBX3RyYWluLCBucm91bmRzID0gMTAwLCB2ZXJib3NlID0gMCkKYGBgCgpgYGB7cn0KSWRlbnRzKGRzMl9BQykgPC0gZHMyX0FDJHNldXJhdF9jbHVzdGVycwpBQ19kYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMl9BQywgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpCkFDX2RhdGEgPC0gQUNfZGF0YVtzZWxlY3RlZF9mZWF0dXJlcyxdCkFDX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczJfQUMpKSkKY29sbmFtZXMoQUNfZGF0YSkgPC0gTlVMTApBQ190ZXN0X2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhBQ19kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gQUNfbGFiZWwpCkFDX3Rlc3QgPC0geGdiLkRNYXRyaXgoZGF0YSA9IEFDX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gQUNfdGVzdF9kYXRhJGxhYmVsKQoKI+mihOa1i+e7k+aenApwcmVkaWN0X3Byb3BfQUMgPC1wcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IEFDX3Rlc3QpICU+JQogbWF0cml4KG5yb3cgPSBsZW5ndGgobGV2ZWxzKElkZW50cyhkczJfUEEpKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gbmNvbChkczJfQUMpLCBieXJvdyA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KGxldmVscyhJZGVudHMoZHMyX1BBKSksY29sbmFtZXMoZHMyX0FDKSkpCgojIyDlvpfliLDliIbnvqTnu5PmnpwKQUNfcmVzIDwtIGFwcGx5KHByZWRpY3RfcHJvcF9BQywyLGZ1bmMscm93bmFtZXMocHJlZGljdF9wcm9wX0FDKSkKSWRlbnRzKGRzMl9BQykgPC0gQUNfcmVzCnVtYXBwbG90KGRzMl9BQykKYGBgCgpgYGB7cn0KZW1iZWRkaW5nIDwtIEZldGNoRGF0YShvYmplY3QgPSBkczJfQUMsIHZhcnMgPSBjKCJVTUFQXzEiLCAiVU1BUF8yIikpCmVtYmVkZGluZyA8LSBjYmluZChlbWJlZGRpbmcsIHQocHJlZGljdF9wcm9wX0FDKSkKCmdnb2JqIDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAwYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgMGApLCBzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMCcsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiM2ZGMwYTYiKSArCiAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDFgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAxYCksc2hhcGU9MTYsIHNpemUgPSAyLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzEnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZTJiMzk4IikgKwogICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgMmA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDJgKSxzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnMicsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNlMmEyY2EiKSArCiAgICAgICAgeGxhYigiVU1BUCAxIikgKyB5bGFiKCJVTUFQIDIiKSAgKwogICAgICAgIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKSkpKSArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKyAKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKZ2dzYXZlKCJkczJfUEF0b0FDX3VtYXAuc3ZnIixkZXZpY2UgPSBzdmcscGxvdCA9IGdnb2JqLGhlaWdodCA9IDgsd2lkdGggPSA4KQpgYGAKCiMjIOWcqGRzMOS4iuiuree7gwpgYGB7cn0KZHMwX2RhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMwLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKZHMwX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczApKSkKCmluZGV4IDwtIGMoMTpkaW0oZHMwX2RhdGEpWzJdKSAlPiUgc2FtcGxlKGNlaWxpbmcoMC4zKmRpbShkczBfZGF0YSlbMl0pLCByZXBsYWNlID0gRiwgcHJvYiA9IE5VTEwpCmNvbG5hbWVzKGRzMF9kYXRhKSA8LSBOVUxMCgpkczBfdHJhaW5fZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGRzMF9kYXRhWywtaW5kZXhdLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gZHMwX2xhYmVsWy1pbmRleF0pCmRzMF90ZXN0X2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhkczBfZGF0YVssaW5kZXhdLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gZHMwX2xhYmVsW2luZGV4XSkKCmRzMF90cmFpbiA8LSB4Z2IuRE1hdHJpeChkYXRhID0gZHMwX3RyYWluX2RhdGEkZGF0YSxsYWJlbCA9IGRzMF90cmFpbl9kYXRhJGxhYmVsKQpkczBfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gZHMwX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gZHMwX3Rlc3RfZGF0YSRsYWJlbCkKCndhdGNobGlzdCA8LSBsaXN0KHRyYWluID0gZHMwX3RyYWluLCBldmFsID0gZHMwX3Rlc3QpCnhnYl9wYXJhbSA8LSBsaXN0KGV0YSA9IDAuMiwgbWF4X2RlcHRoID0gNiwgCiAgICAgICAgICAgICAgICAgIHN1YnNhbXBsZSA9IDAuNiwgIG51bV9jbGFzcyA9IGxlbmd0aCh0YWJsZShJZGVudHMoZHMwKSkpLAogICAgICAgICAgICAgICAgICBvYmplY3RpdmUgPSAibXVsdGk6c29mdHByb2IiLCBldmFsX21ldHJpYyA9ICdtbG9nbG9zcycpCgpic3RfbW9kZWwgPC0geGdiLnRyYWluKHhnYl9wYXJhbSwgZHMwX3RyYWluLCBucm91bmRzID0gMTAwLCB3YXRjaGxpc3QsIHZlcmJvc2UgPSAwKQoKZXZhbF9sb3NzIDwtIGJzdF9tb2RlbFtbImV2YWx1YXRpb25fbG9nIl1dW1siZXZhbF9tbG9nbG9zcyJdXQpwbG90X2x5KGRhdGEuZnJhbWUoZXZhbF9sb3NzKSwgeCA9IGMoMToxMDApLCB5ID0gZXZhbF9sb3NzKSAlPiUgCiAgYWRkX3RyYWNlKHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycytsaW5lcyIsIAogICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gImJsYWNrIiwgbGluZSA9IGxpc3QoY29sb3IgPSAiIzFFOTBGRkM3Iiwgd2lkdGggPSAxKSksCiAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gIiMxRTkwRkY4MCIsIHdpZHRoID0gMikpICU+JSAKICBsYXlvdXQoeGF4aXMgPSBsaXN0KHRpdGxlID0gImVwb2NoIikseWF4aXMgPSBsaXN0KHRpdGxlID0gImV2YWxfbWxvZ2xvc3MiKSkKYGBgCgpgYGB7cn0KaW1wb3J0YW5jZSA8LSB4Z2IuaW1wb3J0YW5jZShjb2xuYW1lcyhkczBfdHJhaW4pLCBtb2RlbCA9IGJzdF9tb2RlbCkKaGVhZChpbXBvcnRhbmNlKQp4Z2IuZ2dwbG90LmltcG9ydGFuY2UoaGVhZChpbXBvcnRhbmNlLDIwKSwgbl9jbHVzdGVycyA9IDEpICsgdGhlbWVfbWluaW1hbCgpCgptdWx0aV9mZWF0dXJlcGxvdChoZWFkKGltcG9ydGFuY2UsOSkkRmVhdHVyZSwgZHMwKSAKYGBgCgpgYGB7cn0KSWRlbnRzKGRzMikgPC0gZHMyJHNldXJhdF9jbHVzdGVycwp0ZW1wIDwtIGdldF9kYXRhX3RhYmxlKGRzMiwgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpCmRzMl9kYXRhIDwtIG1hdHJpeChkYXRhPTAsIG5yb3cgPSBsZW5ndGgocm93bmFtZXMoZHMwX2RhdGEpKSwgbmNvbCA9IGxlbmd0aChjb2xuYW1lcyh0ZW1wKSksIAogICAgICAgICAgICAgICAgICAgYnlyb3cgPSBGQUxTRSwgZGltbmFtZXMgPSBsaXN0KHJvd25hbWVzKGRzMF9kYXRhKSxjb2xuYW1lcyh0ZW1wKSkpCmZvcihpIGluIGludGVyc2VjdChyb3duYW1lcyhkczJfZGF0YSksIHJvd25hbWVzKHRlbXApKSl7CiAgZHMyX2RhdGFbaSxdIDwtIHRlbXBbaSxdCn0Kcm0odGVtcCkKZHMyX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczIpKSkKY29sbmFtZXMoZHMyX2RhdGEpIDwtIE5VTEwKZHMyX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGRzMl9kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gZHMyX2xhYmVsKQpkczJfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gZHMyX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gZHMyX3Rlc3RfZGF0YSRsYWJlbCkKCiPpooTmtYvnu5PmnpwKCnByZWRpY3RfZHMyX3Rlc3QgPC0gcHJlZGljdChic3RfbW9kZWwsIG5ld2RhdGEgPSBkczJfdGVzdCkKCnByZWRpY3RfcHJvcF9kczIgPC0gbWF0cml4KGRhdGE9cHJlZGljdF9kczJfdGVzdCwgbnJvdyA9IGJzdF9tb2RlbFtbInBhcmFtcyJdXVtbIm51bV9jbGFzcyJdXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSBuY29sKGRzMiksIGJ5cm93ID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QoYygwOihic3RfbW9kZWxbWyJwYXJhbXMiXV1bWyJudW1fY2xhc3MiXV0tMSkpLGNvbG5hbWVzKGRzMikpKQoKIyMg5b6X5Yiw5YiG576k57uT5p6cCmRzMl9yZXMgPC0gYXBwbHkocHJlZGljdF9wcm9wX2RzMiwyLGZ1bmMscm93bmFtZXMocHJlZGljdF9wcm9wX2RzMikpCgpjb25mdXNlX21hdHJpeDEgPC0gdGFibGUoZHMyX3Rlc3RfZGF0YSRsYWJlbCwgZHMyX3JlcywgZG5uPWMoInRydWUiLCJwcmUiKSkKc2Fua2V5X3Bsb3QoY29uZnVzZV9tYXRyaXgxLGMoMDo1KSxjKDA6NCksc2Vzc2lvbiA9ICJkczB0b2RzMiIpCgpJZGVudHMoZHMyKSA8LSBmYWN0b3IoZHMyX3JlcyxsZXZlbHMgPSBjKDA6NSkpCnVtYXBwbG90KGRzMikKYGBgCgpgYGB7cn0KZW1iZWRkaW5nIDwtIEZldGNoRGF0YShvYmplY3QgPSBkczIsIHZhcnMgPSBjKCJVTUFQXzEiLCAiVU1BUF8yIikpCmVtYmVkZGluZyA8LSBjYmluZChlbWJlZGRpbmcsIHQocHJlZGljdF9wcm9wX2RzMikpCgpnZ29iaiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgMGA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDBgKSwgc2hhcGU9MTYsIHNpemUgPSAyLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzAnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjNmRjMGE2IikgKwogIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAxYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgMWApLHNoYXBlPTE2LCBzaXplID0gMiwgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCcxJywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2UyYjM5OCIpICsKICAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDJgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAyYCksc2hhcGU9MTYsIHNpemUgPSAyLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzInLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZTJhMmNhIikgKwogICAgIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAzYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgM2ApLHNoYXBlPTE2LCBzaXplID0gMiwgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCczJywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2QxZWJhOCIpICsKICAgICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgNGA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDRgKSxzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnNCcsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNiMWQ2ZmIiKSArCiAgICAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDVgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGA1YCksc2hhcGU9MTYsIHNpemUgPSAyLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzUnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZmQ5OTk5IikgKwogICAgICAgIHhsYWIoIlVNQVAgMSIpICsgeWxhYigiVU1BUCAyIikgICsKICAgICAgICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImNtIikpKSkgKwogICAgICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBOVUxMKSArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsgCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmdnc2F2ZSgiZHMwdG9kczJ1bWFwLnN2ZyIsZGV2aWNlID0gc3ZnLHBsb3QgPSBnZ29iaixoZWlnaHQgPSA4LHdpZHRoID0gOCkKYGBgCgoKYGBge3J9CgpJZGVudHMoZHMxKSA8LSBkczEkc2V1cmF0X2NsdXN0ZXJzCnRlbXAgPC0gZ2V0X2RhdGFfdGFibGUoZHMxLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKZHMxX2RhdGEgPC0gbWF0cml4KGRhdGE9MCwgbnJvdyA9IGxlbmd0aChyb3duYW1lcyhkczBfZGF0YSkpLCBuY29sID0gbGVuZ3RoKGNvbG5hbWVzKHRlbXApKSwgCiAgICAgICAgICAgICAgICAgICBieXJvdyA9IEZBTFNFLCBkaW1uYW1lcyA9IGxpc3Qocm93bmFtZXMoZHMwX2RhdGEpLGNvbG5hbWVzKHRlbXApKSkKZm9yKGkgaW4gaW50ZXJzZWN0KHJvd25hbWVzKGRzMV9kYXRhKSwgcm93bmFtZXModGVtcCkpKXsKICBkczFfZGF0YVtpLF0gPC0gdGVtcFtpLF0KfQpybSh0ZW1wKQpkczFfbGFiZWwgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoSWRlbnRzKGRzMSkpKQpjb2xuYW1lcyhkczFfZGF0YSkgPC0gTlVMTApkczFfdGVzdF9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoZHMxX2RhdGEsImRnQ01hdHJpeCIpKSwgbGFiZWwgPSBkczFfbGFiZWwpCmRzMV90ZXN0IDwtIHhnYi5ETWF0cml4KGRhdGEgPSBkczFfdGVzdF9kYXRhJGRhdGEsbGFiZWwgPSBkczFfdGVzdF9kYXRhJGxhYmVsKQoKI+mihOa1i+e7k+aenAoKcHJlZGljdF9kczFfdGVzdCA8LSBwcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IGRzMV90ZXN0KQoKcHJlZGljdF9wcm9wX2RzMSA8LSBtYXRyaXgoZGF0YT1wcmVkaWN0X2RzMV90ZXN0LCBucm93ID0gYnN0X21vZGVsW1sicGFyYW1zIl1dW1sibnVtX2NsYXNzIl1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IG5jb2woZHMxKSwgYnlyb3cgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChjKDA6KGJzdF9tb2RlbFtbInBhcmFtcyJdXVtbIm51bV9jbGFzcyJdXS0xKSksY29sbmFtZXMoZHMxKSkpCgojIyDlvpfliLDliIbnvqTnu5PmnpwKZHMxX3JlcyA8LSBhcHBseShwcmVkaWN0X3Byb3BfZHMxLDIsZnVuYyxyb3duYW1lcyhwcmVkaWN0X3Byb3BfZHMxKSkKSWRlbnRzKGRzMSkgPC0gZmFjdG9yKGRzMV9yZXMsbGV2ZWxzID0gYygwOjUpKQp1bWFwcGxvdChkczEpCgoKY29uZnVzZV9tYXRyaXggPC0gdGFibGUoZHMxX3Rlc3RfZGF0YSRsYWJlbCwgZHMxX3JlcywgZG5uPWMoInRydWUiLCJwcmUiKSkKc2Fua2V5X3Bsb3QoY29uZnVzZV9tYXRyaXgsYygwOjQpLGMoMDo0KSxzZXNzaW9uID0gImRzMHRvZHMxIikKYGBgCgpgYGB7cn0KZW1iZWRkaW5nIDwtIEZldGNoRGF0YShvYmplY3QgPSBkczEsIHZhcnMgPSBjKCJVTUFQXzEiLCAiVU1BUF8yIikpCmVtYmVkZGluZyA8LSBjYmluZChlbWJlZGRpbmcsIHQocHJlZGljdF9wcm9wX2RzMSkpCgpnZ29iaiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgMGA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDBgKSwgc2hhcGU9MTYsIHNpemUgPSAyLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzAnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjNmRjMGE2IikgKwogIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAxYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgMWApLHNoYXBlPTE2LCBzaXplID0gMiwgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCcxJywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2UyYjM5OCIpICsKICAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDJgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGAyYCksc2hhcGU9MTYsIHNpemUgPSAyLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzInLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZTJhMmNhIikgKwogICAgIG5ld19zY2FsZSgiY29sb3IiKSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBlbWJlZGRpbmdbZW1iZWRkaW5nJGAzYD4wLjEsXSwgCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBgM2ApLHNoYXBlPTE2LCBzaXplID0gMiwgYWxwaGE9MC41KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50KCczJywgbG93ID0gIiNGRkZGRkYwMCIsIGhpZ2ggPSAiI2QxZWJhOCIpICsKICAgICBuZXdfc2NhbGUoImNvbG9yIikgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZW1iZWRkaW5nW2VtYmVkZGluZyRgNGA+MC4xLF0sIAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gYDRgKSxzaGFwZT0xNiwgc2l6ZSA9IDIsIGFscGhhPTAuNSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgnNCcsIGxvdyA9ICIjRkZGRkZGMDAiLCBoaWdoID0gIiNiMWQ2ZmIiKSArCiAgICAgbmV3X3NjYWxlKCJjb2xvciIpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGVtYmVkZGluZ1tlbWJlZGRpbmckYDVgPjAuMSxdLCAKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGA1YCksc2hhcGU9MTYsIHNpemUgPSAyLCBhbHBoYT0wLjUpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoJzUnLCBsb3cgPSAiI0ZGRkZGRjAwIiwgaGlnaCA9ICIjZmQ5OTk5IikgKwogICAgICAgIHhsYWIoIlVNQVAgMSIpICsgeWxhYigiVU1BUCAyIikgICsKICAgICAgICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImNtIikpKSkgKwogICAgICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBOVUxMKSArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsgCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmdnc2F2ZSgiZHMwdG9kczF1bWFwLnN2ZyIsZGV2aWNlID0gc3ZnLHBsb3QgPSBnZ29iaixoZWlnaHQgPSA4LHdpZHRoID0gOCkKYGBgCgpBZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ3RybCtBbHQrSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkN0cmwrU2hpZnQrSyogdG8gcHJldmlldyB0aGUgSFRNTCBmaWxlKS4KClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4K